﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using System.ComponentModel;
using System.Collections.ObjectModel;
using SGCG.Helpers;
using System.Collections.Specialized;

namespace SGCG.Model
{
    public abstract class GraphicObject : NotifyPropertyChanged
    {

        public GraphicObject Parent { get; set; } // TODO ver se precisa de engatilhar evento de mudança

        public GraphicObject(GraphicObject parent)
        {
            this.Parent = parent;
        }

        public abstract void Draw();

        public virtual GraphicObject Attach(IDrawingCanvas drawingCanvas)
        {
            this.DrawingCanvas = drawingCanvas;
            return this;
        }

        protected virtual void RaiseGraphicPropertyChanged(string propertyName)
        {
            base.RaisePropertyChanged(propertyName);

            if (this.DrawingCanvas != null)
            {
                this.DrawingCanvas.SendRedrawRequest();
            }
        }

        public abstract GraphicObject CreateCopy();

        protected void CopyProperties(GraphicObject other)
        {
            other.CurrentTransforms = new ObservableCollection<ITransform>(CurrentTransforms);
        }

        private ObservableCollection<ITransform> currentTransforms = null;
        public ObservableCollection<ITransform> CurrentTransforms
        {
            get
            {
                if (currentTransforms == null)
                {
                    currentTransforms = new ObservableCollection<ITransform>();
                    currentTransforms.CollectionChanged += CurrentTransforms_CollectionChanged;
                }

                return currentTransforms;
            }
            set
            {
                currentTransforms = value;
                RaiseGraphicPropertyChanged("CurrentTransforms");
            }
        }

        void CurrentTransforms_CollectionChanged(object sender, NotifyCollectionChangedEventArgs ea)
        {
            switch (ea.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var item in ea.NewItems)
                    {
                        ((ITransform)item).TransformChanged +=
                            (s, e) => RaiseGraphicPropertyChanged("CurrentTransforms");
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (var item in ea.OldItems)
                    {
                        ((ITransform)item).TransformChanged -=
                            (s, e) => RaiseGraphicPropertyChanged("CurrentTransforms");
                    }
                    break;
            }

            RaiseGraphicPropertyChanged("CurrentTransforms");
        }

        /// <summary>
        /// Consolidates a transform permanently into the object.
        /// </summary>
        /// <param name="transform">the transform to be consolidated</param>
        public abstract void ConsolidateTransform(ITransform transform);

        /// <summary>
        /// Transforms a point through all the current transforms and the
        /// transforms of the parent objects.
        /// </summary>
        /// <param name="p">the point to be transformed</param>
        /// <returns>the transformed point</returns>
        public Point TransformPoint(Point p)
        {
            foreach (var transform in CurrentTransforms)
                p = transform.Transform(p);
            if (Parent != null)
                p = Parent.TransformPoint(p);
            return p;
        }

        public void TransformAngle(double angleInit, double angleSpan, out double newAngleInit, out double newAngleSpan)
        {
            foreach (var transform in CurrentTransforms)
                transform.Transform(angleInit, angleSpan, out angleInit, out angleSpan);
            if (Parent != null)
                Parent.TransformAngle(angleInit, angleSpan, out angleInit, out angleSpan);
            newAngleInit = angleInit;
            newAngleSpan = angleSpan;
        }

        private bool _selected;
        public virtual bool Selected
        {
            get { return _selected; }
            set
            {
                if (_selected != value)
                {
                    _selected = value;
                    RaiseGraphicPropertyChanged("Selected");
                }
            }
        }

        public bool Deleted { get; set; }

        protected IDrawingCanvas DrawingCanvas
        {
            get;
            private set;
        }
    }
}
